TwistedPixel Preset Development Guide v1.0
Copyright © 2003 Brian Spangler, All rights reserved.
http://www.outdoorgrace.com

*portions of this document are excerpts included with permission from
 the Beginners Guide to MilkDrop Preset Writing developed by John Rovastar at www.milkdrop.co.uk

 

Usage
    Introduction
    Requirements
    Installing
    Keyboard Shortcuts
    Troubleshooting
    Acknowledgements

Presets
    Scripting
    A Preset's Lifetime
    Color Cycling
    Variables -- A Basic Tutorial
    Per Frame Equations
    Per Pixel Equations
    Function Reference
    Custom Waveforms
    Custom Shapes
    Conversion Utility

     Debugging With Microsoft's Script Editor

Usage

Introduction

TwistedPixel is a WinAMP and WMP Visualization that uses a video card's 3d capabilities and Microsoft's Direct3D to produce flowing images in reaction to music. How TwistedPixel differs from other visualizations is that there exists no hardcoded visuals, all the imagery produced is done so via the interpretation of presets files that anyone can write. A few hundred preset files have been included with the installation of TwistedPixel so as to allow immediate viewing of the visualization. Feel free to write your own and share them with the preset authoring community.


A little history, the first visualization that I encountered that offered the use of presets was NullSoft's Milkdrop. At that time I had just released PixelTrip and knew immediately I was already behind the pack of visualization development. Since that time I have worked periodically on TwistedPixel until it had reached the same levels of Milkdrop and even supported Milkdrop preset files. Initially the entire vis was developed using C++ and MMX so as to be able to process the necessary time variable ... although the vis looked quite similar to Milkdrop it lacked the performance and levels of color and alpha blending. It was Roman Komary and his excellent vis X-Plugin that turned my attention to D3D and the advantages of its use; since that time TwistedPixel has been redeveloped with D3D. Once the project was completed I did a very unusual thing...I shelved it.
With Milkdrop, at the time, in its 1.3 release I saw no reason to put out what was basically a clone that I had developed to see if I could and that offered very little functionality over the original. Since then I have been involved in developing projects that utilize varying scripting technologies and for the first time I saw something that could be added to TwistedPixel that would provide an appeal over Milkdrop -- you can now develop your presets using VBScript, JavaScript, TCL, Pearl and Python. By adding support for full blown scripting languages the door is open to a whole new breed of visualization presets. To help existing preset authors port Milkdrop presets to VBScript or JavaScript there is included in the installation a Milkdrop To TwistedPixel converter that works on a single preset or an entire folder of presets.

 

Requirements

 

Installing

In time a formal installer will be developed for TwistedPixel, the TwistedPixel SDK and all the supported presets to date. For now there is little more than a zip file containing the files and the directory structure necessary to run on WinAMP or WMP.
If you are a WinAMP user and have it installed in your ..\Program Files\ folder then unzipping to the WinAMP folder will be enough to establish the folder hierarchy necessary.
If you are a WMP user then you can unzip the files to the WMP visualizations folder and then register the component using the utility regsvr32 ... e.g.

regsvr23 TwistedPixel.dll

The folder hierarchy used in release 1.0 of TwistedPixel follows (where <> denotes a folder) :

    <WinAMP>
        <PlugIns>
            vis_tp.dll
            <TwistedPixel>
                preset files...
            <TwistedPixel SDK>
                'this guide'.html
                MD2TP.exe
 

   <Windows Media Player>
        <Visualizations>
            TwistedPixel.dll
            <TwistedPixel>
                preset files...
            <TwistedPixel SDK>
                'this guide'.html
                MD2TP.exe               
           

Once installed, run WinAMP and press Ctrl-K or run WMP and use the menus to select configure. On configure, the TwistedPixel configuration window dialog will be presented for you to make any selections beyond the defaults.

 

Keyboard Shortcuts

While TwistedPixel is running you can use keyboard shortcuts to change behaviors of the visualization.
Keyboard Shortcut f toggle display of Frames Per Second
Keyboard Shortcut d toggle beat detection
Keyboard Shortcut p display name of current preset
Keyboard Shortcut n load next preset
Keyboard Shortcut o open a specific preset
Keyboard Shortcut r toggle random selection of preset
Keyboard Shortcut i flag the current preset as incomplete

A note about shortcut key i:
If you are converting many Milkdrop presets to VBScript or JavaScript using the MD2TP.exe utility then this shortcut feature is a quick way to flag those presets that did not convert as expected. The basic idea is to convert a folder of Milkdrop presets where the output folder is an empty TwistedPixel folder (review folder hierarchy above). Once the newly converted presets are populating the TwistedPixel folder, launch WinAMP or WMP and begin to proof the conversions. Use the 'n' key to move forward through the list of presets and use 'i' to flag the ones that failed conversion. Pressing 'i' will rename the preset file with the prefix 'incomplete.conversion.' which is very easy to identify in Windows Explorer.

 

Trouble Shooting

The following are suggestions you can take in the event of a visualization failure.
First determine if the problem you are experiencing is a problem in TwistedPixel or in visualizations and video in general. An easy approach to doing this is to simply try a different Direct3D visualization, if it works then the problem you are experiencing is most likely an issue with TwistedPixel and so you should contact me with a description of the issue. Please do not be offended if I do not get back to you in a quick manner -- I receive too many emails in a given day to read/respond to them all.
If you have determined that there is likely a problem with your video then try any of the following:

Acknowledgements

There are few to whom I owe some thanx that if not for there help I would never have completed Twisted Pixel. To Dave Wolfe, a thanx in testing and providing feedback. To Roman Komary who turned the light on for me regarding Direct3D and who answered many questions about the uses of Milkdrop variables. To Rovastar for the influence in completing this work and for keeping me informed about the vis community. And of course a thank you to Ryan Geiss whose original development of Milkdrop inspired me to try and write one too.

 


Authoring Guide

 

Scripting

As already mentioned, one of the greatest assets to the TwistedPixel visualization is its ability to support preset files written in VBScript, JavaScript, Python, Pearl and TCL. To determine the scripting language in use for a given preset TwistedPixel will use the file extensions .vbs, .js, .py, .tcl and .pls to load the appropriate support. By default the Windows installs the scripting language runtime support for VBScript and JavaScript, for Python, Pearl and TCL you will need to install their scripting language runtime files which are available for download from a number of web sites.

Four things are useful for writing presets: Mathematics knowledge, artistic flare, persistence and luck. If you have any of these you will be able to create a basic preset and with greater capabilities of course comes more elaborate presets.
When it comes to the basic mathematics knowledge you can find a number of sites that delve into the uses of sin, cos and tan as well as coordinate systems cartesian and polar.
You can look at existing presets and randomly copy things into your own to view the results and possibly get a preset you like, but if you actually understand what is happening with regards to the mathematics then you will be able to elaborate on the preset code and adjust to produce the results you desire. In time I intend to release a UI development environment that allows you to graphically build a preset and which will generate the necessary code for you.

A quick note about the scripting editor I use...
Although notepad is doable I prefer features such as syntax coloring and code completion etc. After having looked at a dozen or so script editors for VBScript and JavaScript I came upon one that I really like and that is built into Microsoft Office titled 'Microsoft Script Editor'. If you install Microsoft Office you will probably not find any mention or link to the editor anywhere (why? I am not sure). If you used all the default settings during the installation then you can find the script editor at "C:\Program Files\Microsoft Office\Office10\MSE7.EXE"; from Window's Explorer right click on the file and click on 'Create Desktop Shortcut'.

Preset's Lifetime

During a preset's lifetime the TwistedPixel visualization will invoke a series of methods that are available in your preset. It is in the body of these methods that you develop your code the ultimately alters the behaviors of the pixels on the screen in response to sound. Only two of these methods are required, the remaining methods are optional and will only be invoked by TwistedPixel when found at the time of loading and evaluating the preset. The methods are as follows in VBScript syntax, syntax will differ depending on your scripting language:

Sub PresetInitialize() Called when the preset is first loaded. Required.
It is in this method that all variables should be initialized including those specific to TwistedPixel
 
Sub ProcessPerFrame() Called once for each frame that is rendered to the screen. Required.
 
Sub ProcessPerPixel() Called once for each pixel that makes up a frame. Optional.
 
Function ProcessWaveform() Called once per frame when TwistedPixel is rendering waveforms to the screen.
In the body of this method you call the TwistedPixel function AddWaveformElement for each element of a waveform you want to add to the screen (more about this later).

Returning True to TwistedPixel is an indicator that you want to build another waveform and so TwistedPixel will render the waveform you built and call ProcessWaveform for the next preset waveform.

Returning False to TwistedPixel is an indicator that no more waveforms will be added to this frame.
 
Function ProcessCustomShape() Called once per frame when TwistedPixel is rendering custom shapes to the screen.
In the body of this method you set preset variables specific to shapes (more about this later) that are to be displayed on the screen.

Returning True to TwistedPixel is an indicator that you want to add more shapes and so TwistedPixel will render the current shape and call ProcessCustomShape for the next preset shape.

Returning False to TwistedPixel is an indicator that no more shapes will be added to this frame.
 
Sub PresetException(ByVal eCode, ByRef eDescription) Called when a script error has occurred. In addition the script error will be presented in the lower left hand corner of the screen.
 
Sub PresetTerminate() Called when the preset is unloading. Used for clean up of any resources your preset allocated.

 


 

Color Cycling

One of the first things people want to do is to have different colors on the screen, that is via waveforms, borders, custom shapes etc. The simplest way to do this is add some simple formulas to the ProcessPerFrame method. Now, let's say you wanted to make the color of the waveform, sound wave that gets plotted on the screen, vary through time. Color, relative to Windows, is defined by three components, these are red, green, and blue; each is in the range 0 to 1  where 0 is empty and 1 is full intensity. Adding the following to the ProcessPerFrame method (in JavaScript):


tp_wave_r += 0.5 * Math.sin(tp_time*1.13);
tp_wave_g += 0.5 * Math.sin(tp_time*1.23);
tp_wave_b += 0.5 * Math.sin(tp_time*1.33);

Now once per rendered frame the three color components will change or cycle in time at an amplitude between .5 and 1.0 and at a frequency of either 1.13, 1.23 or 1.33 -- by using varying frequencies you ensure that the cycling of each component occurs a different times which produces a broader range of color. If you were to use the same frequency for all then only one color level, gray, would be recognized.
Sine (and cosine) waves have a range of -1 to 1 so if to use these functions within an allowable range of 0 thru 1 you will have to modify them.


0.5 + 0.5 * Math.Sin(tp_time) = 0.5 + (-0.5 to 0.5) = 0 to 1;
 

This will generate the range 0 to 1 and then 1 to 0, oscillation, over a period of 6.28 seconds (the value of PI*2) for one complete cycle. If you want to oscillate or cycle at a different time interval then multiplying the TwistedPixel time variable, tp_time, will change the 6.28 period. For example the following 0.5 * sin(2 * tp_time) will produce a time period is now 6.28/2 = 3.14 seconds which is a shorter time interval that causes the cycling to appear faster. To slow down the cycling you would divide vs. multiply.
If you want the color variables to be focused on a stricter range of color, lets say red, then you could alter the equation as follows 0.75 + 0.25 * Math.Sin(tp_time) = 0.75 + (-0.25 to 0.25) = 0.5 to 1. This limits the red component to upper values  of .75 thru 1.0.
Also you can base the color output on the TwistedPixel sound variables tp_bass, tp_treb_att, etc. to create waveform colors in reaction to what the music is doing.
 

 

Variables -A Basic Tutorial

There are an endless number of effects that can be produced with TwistedPixel. The most minor changes to any one of the TwistedPixel variables can change the entire look of a preset which is generally the most common way of developing new presets.

TwistedPixel variables are those values you see in the script that are prefixed with the values 'tp_' -- although MilkDrop variables do not have this prefix it is very easy to add the prefix if you are porting a MilkDrop preset since the remaining part of the value is identical to those supported in MilkDrop. For example: nWaveMode is tp_nWaveMode.
These variables are hosted internally by TwistedPixel and thru the magic of the scripting engine they are published for you to alter as necessary to achieve a desired effect. Please note that not all variables are read/write -- there are those variables specific to the geometry of the screen and so they cannot be written to, such as tp_x, tp_y, tp_rad and tp_ang.
 

The following lists those preset variables you can write to and that are initialized in your PresetInitialize() method

Name

Range

Description

     
tp_nWaveMode 0 ... 7 Controls which of the 8 types of built in waveform is to be drawn
When providing custom waveforms you can set this value to -1 which will shut down the internal waveform display so as to give your custom waveform all the screen realestate.
tp_fWaveScale > 0 Amount to scale a waveform. (1) normal, (> 1) scale larger (< 1) scale smaller.
tp_fWaveAlpha > 0 Opacity level of the waveform. (0) transparent (1) opaque
tp_fWaveSmoothing > 0 Amount to apply a smoothing algorithm to the waveform.
tp_wave_x 0 ... 1 Position of the waveform: (0) far left edge of screen (0.5) center (1) far right
tp_wave_y 0 ... 1 Position of the waveform: (0) bottom of screen (0.5) center (1) top of the screen
tp_wave_r 0 ... 1 Red Component of the wave form
tp_wave_g 0 ... 1 Green Component of the wave form
tp_wave_b 0 ... 1 Blue Component of the wave form
tp_bWaveDots 0 or 1 When the value is non zero the internal waveforms will be drawn as a series of dots.
When the value is zero then the waveform will be drawn as a series of lines.
tp_bWaveThick 0 or 1 When the value is non zero the waveform elements are drawn at twice there regular thickness.
When the value is zero the waveform is drawn at its default thickness.
tp_bModWaveAlphaByVolume 0 or 1 When the value is non zero the waveform elements opacity is dependent on the volume levels.
When the value is zero the waveform elements opacity is independent of volume.
tp_fModWaveAlphaStart 0 ... 1 If tp_bModWaveAlphaByVolume is enabled then this value dictates the start alpha value to use
tp_fModWaveAlphaEnd 0 ... 1 If tp_bModWaveAlphaByVolume is enabled then this value dictates the end alpha value to use
tp_bAdditiveWaves 0 or 1 When the value is non zero the waveform will be added to the screen's existing pixels.
When the value is zero the waveform will be drawn over the existing pixel.
tp_bMaximizeWaveColor 0 or 1 Utilize the color component with the highest value to scale the other two color components thus providing a new color that is brighter and more saturated.
tp_bWaveOnlyLeft 0 or 1 Use just the left music channel in building the preset waveform
tp_bWaveOnlyRight 0 or 1 Use just the right music channel in building the preset waveform
tp_ob_size 0 ... .5 Thickness of the outer border drawn at the edges of the screen per each frame
tp_ob_r 0 ... 1 Red Component of the motion vector
tp_ob_g 0 ... 1 Green Component of the motion vector
tp_ob_b 0 ... 1 Blue Component of the motion vector
tp_ib_size 0 ... .5 Thickness of the inner border drawn at the edges of the screen per each frame
tp_ib_r 0 ... 1 Red Component of the motion vector
tp_ib_g 0 ... 1 Green Component of the motion vector
tp_ib_b 0 ... 1 Blue Component of the motion vector
tp_bMotionVectorsOn 0 or 1 Enable motion vectors -- this variable is no longer used. A zero or non zero value for tp_mv_a dictates motion vector visibilty.
tp_nMotionVectorsX 0 ... 64 The number of illuminated pixels to tile across the horizontal. A maximum of 64 is allowed.
tp_nMotionVectorsY 0 ... 48 The number of rows to apply tp_nMotionVectorsX in the vertical direction.
A maximum of 48 is allowed.
tp_mv_a 0 ... 1 The level of opacity applied to the motion vectors.
tp_mv_l 0 ... 5 Trail length of each motion vector (not currently implemeted).
tp_mv_dx -1 ... 1 Horizontal placement offset of the motion vectors.
tp_mv_dy -1 ... 1 Vertical placement offset of the motion vectors.
tp_mv_r 0 ... 1 Red Component of the motion vector
tp_mv_g 0 ... 1 Green Component of the motion vector
tp_mv_b 0 ... 1 Blue Component of the motion vector
tp_zoom > 0 Controls inward/outward motion. (0.9) zoom out 10% per frame (1.0) no zoom (1.1) zoom in 10%
tp_fZoomExponent > 0 Controls the curvature of the zoom (1) normal
tp_warp > 0 Controls the magnitude of the Milkdrop warping (0) none (1) normal (2)major warping etc.
tp_fWarpScale > 0 Amount to scale warp effect
tp_fWarpAnimSpeed > 0 Frequency or speed of warp effect
tp_rot   Controls the amount of rotation. (0) none (0.1) slightly right (0.1) slightly clockwise (0.1) CCW
tp_sx > 0 Controls amount of constant horizontal stretch (0.99)shrink 1% (1)normal (1.01)stretch 1%
tp_sy > 0 Controls amount of constant vertical stretch (0.99)shrink 1% (1)normal (1.01)stretch 1%
tp_cx 0 ... 1 Controls where the center of rotation and stretching is in the horizontal. (0) left (0.5) center (1) right
tp_cy 0 ... 1 Controls where the center of rotation and stretching is in the vertical. (0) top (0.5) center (1) bottom
tp_dx   Controls amount of constant horizontal motion (-0.01)  move left 1% per frame, (0) none, (0.01) move right 1%
tp_dy   Controls amount of constant vertical motion (-0.01)  move up 1% per frame, (0) none, (0.01) move down 1%
tp_fDecay 0 ... 1 Controls the fade to black levels. (1) no fade (0.9) strong fade (0.98) recommended
tp_bDarkenCenter 0 or 1 Not implemented in TwistedPixel
tp_fGammaAdj

> 0

Controls the level of brightness. (1) normal (2) double (3) triple, etc.
tp_fVideoEchoAlpha 0 ... 1 Controls the opacity of a second graphics layer where 1 is opaque and 0 is transparent.
tp_fVideoEchoZoom > 0 Controls the size of a second graphics layer when fVideoEchoAlpha is > 0
tp_nVideoEchoOrientation 0 ... 3 Four positions to place the preset frame as a second graphics layer to 'echo' the first.
(0) normal (1) flip on x (2) flip on y (3) flip on both
tp_bTexWrap 0 or 1 For pixels that fall off the screen, they will be wrapped around the other side vs being clipped internally from the video buffer. Utilizing the wrapping of pixel values can produce some of the better presets.
tp_bRedBlueStereo 0 or 1 Get your movie time 3D glasses out...this enables 3D movie mode where the waveform will be displayed in the red and blue-green needed to give the effect of 3D.
tp_bInvert 0 or 1 Invert filter applied when value is 1
tp_bBrighten 0 or 1 Brighten filter applied when value is 1
tp_bDarken 0 or 1 Darken filter applied when value is 1
tp_bSolarize 0 or 1 Solarize filter applied when value is 1
tp_fDecay_Red 0 ... 1 red decay value to be multiplied by tp_fDecay and applied to red channel in processing decay
tp_fDecay_Green 0 ... 1 green decay value to be multiplied by tp_fDecay and applied to green channel in processing decay
tp_fDecay_Blue 0 ... 1 blue decay value to be multiplied by tp_fDecay and applied to blue channel in processing decay
tp_bEnableBeatDetection 0 or 1 Enable TwistedPixel internal beat detection algorithm
tp_bBeatDetected 0 or 1 A read-only per frame variable: (0) no beat has been detected, (1) a beat has been detected
tp_nBeatDetectionSensitivity > 0 A value used to set a threshold in determining if a given frame has a beat. Default value is 192.
tp_fDuration > 0 The number of seconds the preset is to run before TwistedPixel loads the next preset.
tp_fDesiredFPS > 0 The average number of frames per second the preset is to run at. You can change this value during the duration of the presets playing time to provide varying effects such as speeding up and slowing down in response to volume.
tp_NextPreset String A string value containing fully qualified path/filename of the next preset to load
This is useful if chaining multiple presets together in one overall presentation.

If no value is provide then TwistedPixel will automatically select the next preset.
tp_volume > 0 A per frame volume amount as calculated by TwistedPixel
tp_TextureWidth   Read Only, Internal D3D texture size
tp_TextureHeight   Read Only, Internal D3D texture size
tp_MeshX, tp_MeshY 8 ... 128 Read/Write per frame internal number of mesh points used in calculating per pixel values
The higher the value the greater the image quality but the greater the performance penalty

Modes generally set in PresetInitialize: (8,6) (16,12) (24,18) (32,24) (40,30) (48,36) (96,72)

     

The following lists those preset variables that change per frame and are read only

Name

Range

Description

     
tp_time > 0 Retrieves the current time, in seconds, since TwistedPixel started running
tp_bass > 0 Retrieves the current amount of bass. 1 is normal; below ~0.7 is quiet; above ~1.3 is loud bass
tp_mid > 0  ... same as tp_bass but for mid range frequencies
tp_treb > 0  ... same as tp_bass but for high frequencies
tp_bass_att > 0 Retrieves an attenuated reading on the tp_bass, meaning that it is damped in time and doesn't change so rapidly.
tp_mid_att > 0  ... same as tp_bass_att but for tp_mid
tp_treb_att > 0  ... same as tp_treb_att but for tp_treb
tp_fps > 0 Average number of frames per second
tp_frame > 0 The frame count since TwistedPixel started
tp_progress 0 ... 1 Progress through the current preset
If the preset was just loaded then this value is closer to 0
If the preset is about near ending then this value is closer to 1
     

 

Name

Range

Description

     
tp_x

0 ... 1

Retrieves the x-position of the current pixel.
At the left edge of the screen the value is 0, the middle value is 0.5 and the right edge value is 1
tp_y

0 ... 1

Retrieves the y-position of the current pixel.
At the top of the screen the value is 0, the middle value is 0.5 and the bottom value is 1
tp_rad

0 ... 1

Retrieves the distance of the pixel from the center of the screen.
At the center of the screen the value is zero and the corners have a value of 1
The middle of the edges will be 0.707 which is half of the square root of 2
tp_ang

0 ... 6.28

Retrieves the angle of the current pixel with respect to the center of the screen.
If the point is to the right of the center then the value is zero.
Above center is PI/2 or 1.57
To the left is PI or 3.14
Below center is 4.71 or PI*3/2
Below and to the right of the center the value will approach 6.28 or PI*2
 
*this is simply the arctangent of y over x precomputed

 

Per Frame Processing
 

From the color cycling section above we've already seen an example of the type of code you can add to per frame processing.
Now lets change zoom and rotation a bit to see some per frame processing...

Keeping in mind that the sin function always returns a value between -1 and 1 the following line of code will cause the screen to zoom in and out over the course of time

    tp_zoom += 0.1 * Math.Sin(tp_time);

The amount of zoom would oscillate between the values 0.9 and 1.1 over a period of 6.28 seconds. If you want per frame tp_zoom to cycle every 2 seconds, you would multiply tp_time by PI.

    tp_zoom += 0.1 * Math.Sin(tp_time*3.14);

This is just a sampling of what can be completed within the body of ProcessPerFrame method. Below is an example of what is Default.vbs which is a generic VBScript preset used to demonstrate some of the code in this document.

'''
' Called per frame
'''
Sub ProcessPerFrame
    ' get the time -- make just one trip per-frame to do it in
    fT = tp_time

    tp_wave_r = tp_wave_r + .4 * ( .6 * Sin(0.933 * fT) + .4 * Sin(1.045 * fT) )
    tp_wave_g = tp_wave_g + .4 * ( .6 * Sin(0.900 * fT) + .4 * Sin(0.956 * fT) )
    tp_wave_b = tp_wave_b + .4 * ( .6 * Sin(0.910 * fT) + .4 * Sin(0.920 * fT) )

    tp_zoom = tp_zoom + 0.1 * Sin(fT)
End Sub
 

Per Pixel Processing

Per frame processing is an activity that affects the entire screen ... what if you want to change the rotation amount for a specific area of the screen or you want to zoom just a portion of the screen, this is where per pixel processing is used. Lets look at the following code:

'''
' Called per pixel
'''
Sub ProcessPerPixel
    ' apply per-pixel rotation
    tp_rot = tp_rot + 0.16 * Sin(tp_time * -3.3 + tp_rad * 11)*(1.3 - tp_rad)
End Sub

The above code will use the radius relative to the x, y, position for this per pixel call and rotate that pixel a calculated amount that will be different from other positions on the screen. This approach makes for some fantastic presets.

It is now time to talk about the woes of per pixel processing ... this is the one method call in your preset that in itself occupies the most CPU time and can make or break performance in your preset.
Unlike Milkdrop and prior versions of TwistedPixel where an internal expression parser could handle the expense of per pixel processing, this version of TwistedPixel exposes the variables via a scripting engine that requires more than one trip per use of a variable. This is the greatest deficit in per pixel processing for this visualization. Given this issue you will want to limit the number of trips to obtain TwistedPixel variable values. For example in the above code the following changes should be made:

These small optimizations go a long away to producing more frames per second. The changes mentioned would appear:

'''
' Called per pixel
'''
Sub ProcessPerPixel
    Dim fRad

    fRad = tp_rad

    ' apply per-pixel rotation
    tp_rot = tp_rot + 0.16 * Sin(m_fT * -3.3 + fRad * 11)*(1.3 - fRad)

End Sub

Function Reference

Included with TwistedPixel are a series of functions that will enhance your preset development. Some of these functions are provided more for backward compatibility with Milkdrop -- it is preferred that presets for TwistedPixel limit the use of these functions and instead replace the use with code appropriate to that scripting languages environment. For example: Milkdrop functions above, below and equal would be replaced with basic if then else logic.

DebugOut("some variable") A useful method to send debug data to the screen while you develop your preset. Any string value can be used and can include tab and carriage return line feeds.
AddWaveformElement Use in adding elements to a custom waveform -- see section on custom waveforms
WaveformLeft Returns an array of left channel waveform data for the given frame. Use LBound and UBound to define array boundaries. See preset Spangler - FunColors.vbs for a demonstration.
WaveformRight Returns an array of right channel waveform data for the given frame. Use LBound and UBound to define array boundaries. See preset Spangler - FunColors.vbs for a demonstration.
SpectrumLeft Returns an array of left channel specturm data for the given frame. Use LBound and UBound to define array boundaries.
SpectrumRight Returns an array of right channel specturm data for the given frame. Use LBound and UBound to define array boundaries.
Above(A,B) Returns 1 if A > B, else 0
acos(A) Arc-cosine of A. Returns the angle, measured in radians, whose cosine is A
acosh Same as acos() but for hyperbolic cosine
asin(A) Returns the angle, measured in radians, whose sine is A
asinh Same as asin() but for hyperbolic sine
atan(A) Returns the angle, measured in radians, whose tangent is (A)
atan2(A,B) Returns the Arc-tangent of (A/B), measured in radians
atanh(A) Same as atan() but for hyperbolic tangent
band(A,B) Boolean and, returns 1 if A and B are not equal to 0
bor(A,B) Boolean or, returns 1 if A or B are not equal to 0
bnot(A) Boolean not, returns 1 if A = = 0 or 0 if A != 0
below(A,B) Returns 1 if A < B, else 0
besselJ0(A) Bessel function of the first kind, order 0
besselJ1(A) Bessel function of the first kind, order 1
besselJn(A,B) Bessel function of the first kind, order n
besselY0(A) Bessel function of the second kind, order 0
besselY1(A) Bessel function of the second kind, order 1
besselYn(A) Bessel function of the second kind, order n
deg2rad(A) Convert A from radians to degrees
ceil(A) Ceiling of A. Returns the smallest integer greater than A. Rounds up to the next higher integer
chgsign(A) Reverses the sign of A; e.g. -A return A, A returns -A
copysign(A,B) Copy the sign of B returning A; e.g. copysign(5, -2) return -5 and copysign(-5,2) returns 5
cosh(A) Same as cos() but for hyperbolic cosine
exp(A) Exponential of A. Returns the value of e raised to the power A where e is the base of the natural logarithm, i.e. the non-repeating value approximately equal to 2.71828182846. More simply, the same as pow(e, A), where e is Euler's constant (2.7182..., the base of natural logarithms)
equal(A,B) Returns 1 if A = B, else 0
floor(A) Floor of A. Returns the largest integer less than A. Rounds down to the next lower integer
frac(A) Returns just fractional portion of the number
hypot(A,B) Returns the length of the hypotenuse of a right triangle, given the length of the two sides A and B. This call is equivalent to sqrt(A*A+B*B)
iif(A,B,C) If int(A) differs from 0, the return value of this function is B, else C. Only the parameter which needs to be evaluated is evaluated, the other parameter is skipped
int(A) Returns integer value of A
log10(A) Returns the log base 10 of the value A
max(A,B) If A>B, the result is A, else B
min(A,B) If A<B, the result is A, else B
pow(A,B) Returns A to the power of B
rand(A) Returns a random integer modulo A; e.g. rand(4) will return 0, 1, 2, or 3
randf(A) Returns a random float modulo A; e.g. randf(3.99) may return 0.233, 1.5, 2.22, or 3.8
rad2deg(A) Convert A from degrees to radians
round(A) Rounds to integer portion with rounding.
If value negative then floor is used, if value is positive then ceil is used
Similar to trunc, but different rounding behavior
sign(A) Returns the sign of A or 0 < 0 = -1, > 0 = 1, 0 = 0
sinh(A) Same as sin() but for hyperbolic sine
pow2(A) Returns the square of A
tanh(A) Same as tan() but for hyperbolic tangent
trunc(A) Truncates to integer portion with rounding.
If value negative then ceil is used, if value is positive then floor is used
Similar to round, but different rounding behavior

 

Custom Waveforms

With this release of TwistedPixel you can build custom waveforms to be rendered to the screen during per frame processing.
A good example of this feature is in the FunColor.vbs preset. It is recommended you review this preset after reading this section.

The method ProcessWaveform() is called when TwistedPixel is rendering waveforms to the screen. In the body of this method you add your code to provide TwistedPixel with custom waveforms on behalf of your preset.
A waveform is nothing more than a series of dots strung together at varying positions across the screen -- if you have ever seen an oscilloscope or the tech gadgets on one of the Star Trek series then you've no doubt come across a waveform. To add your own waveform you use the TwistedPixel function AddWaveformElement which sets one pixel element for the waveform you are drawing. Once you have called AddWaveformElement and set all of the pixel elements for the waveform exit the ProcessWaveForm function and TwistedPixel will apply the pixel elements to the screen.
With this arrangement there is no limit on the number of custom waveforms you can add to the screen ... of course the more you add the longer you delay the display of the current frame. The process flow to expect when adding custom waveforms is as follows:

    TwistedPixel is notified a frame is to be rendered
        TwistedPixel obtains audio information and makes it available to the preset
        The presets ProcessPerFrame is invoked and processed
        Any internal waveforms and motion vectors are processed
        The presets ProcessWaveForm is invoked
            TwistedPixel processes the waveform elements and renders them to the screen
            The return value from ProcessWaveForm is evaluated.
                If the return value is True then TwistedPixel loops and again calls ProcessWaveForm for the next custom Waveform
                If the return value is False then TwistedPixel leaves the custom waveform processing
            TwistedPixel leaves the waveform Processing
        Any custom shapes are rendered
    TwistedPixel presents the frame
 
The AddWaveformElement function requires the following parameters...

AddWaveformElement nOrdinal, xPos, yPos, red, green, blue, alpha
nOrdinal -- the index value of the pixel element. index values are 0 thru number of elements - 1.
xPos -- the x position on the screen in the range of 0 thru 1
yPos -- the y position on the screen in the range of 0 the 1
red -- the red component of the pixel element in the range of 0 thru 1
green -- the green component of the pixel element in the range of 0 thru 1
blue -- the blue component of the pixel element in the range of 0 thru 1
alpha -- the alpha component of the pixel element in the range of 0 thru 1

The body below is an example of plotting pixels on the screen from the sample vis Spangler - FunColors.vbs.

'''
' Request to build a custom waveform
'
' returning True to TwistedPixel is an indicator we want to build another
' returning False to TwistedPixel is an indicator we are done building waveforms for this m_fRedame
'''
Function ProcessWaveform


Dim i, nSample, nLength
Dim nIndex, fIndexfact, fVal
Dim fx, fy
fx = fy = CSng(0)

' only adding one waveform to the screen so return false after this call
ProcessWaveform = False

' Available byte array audio channels are WaveformRight, WaveformLeft, SpectrumRight & SpectrumLeft
' get the left audio channels of waveform data from WinAMP
Dim arrWaveformLeft
arrWaveformLeft = tp_WaveformLeft

' dampen the WinAMP waveform data to meet screen requirements
DampenWinAMPWaveForm arrWaveformLeft, m_fScale

' set preset params for display of this waveform
' *these preset vars are reset per-frame and so their setting here is specific to the addition of this waveform
tp_bMaximizeWaveColor=1

' iterate the number of elements we want to add to the waveform and set x, y positions
nLength = CInt(m_fWidth - 1)
For nSample = 0 to nLength

nIndex = CInt(nSample * m_fIndxFact)

' get the x,y positions to plot this point and scale it to [0..1]
fx = CSng(nSample * m_fScalefactHorz)
fy = .5 + (m_fScalefactVert * CSng(arrWaveformLeft(nIndex)))

' add the waveform element to the waveform
nIndex = CInt((m_nOffset + nSample) Mod NUMSAMPLES)
AddWaveformElement nSample, fx, fy, _
m_pRed(nIndex), _
m_pGreen(nIndex), _
m_pBlue(nIndex), _
m_fAlpha

Next

End Function
 

 

Custom Shapes


Like Custom Waveforms you can also add your own custom shapes to the screen as each frame is rendered.  The following processflow is the step by step of the order TwistedPixel uses to obtain and display your preset's custom shapes.
 

    TwistedPixel is notified a frame is to be rendered
        TwistedPixel obtains audio information and makes it available to the preset
        The presets ProcessPerFrame is invoked and processed
        Any internal waveforms and motion vectors are processed
        Any custom waveforms are rendered
        The presets ProcessCustomShape is invoked
            TwistedPixel processes the custom shape properties and renders the shape to the screen
            The return value from ProcessCustomShape is evaluated.
                If the return value is True then TwistedPixel loops and again calls ProcessCustomShape for the next custom shape
                If the return value is False then TwistedPixel leaves the custom shapes processing
            TwistedPixel leaves the shapes Processing
        TwistedPixel completes shapes processing
    TwistedPixel presents the frame
 

There is no limit to the number of shapes you can add to the screen. A custom shape is an n-sided polygon with properties such as radius, position, inner, outer and border colors as well as a rotation value and a texture property. Unlike custom waveform where there is a specific function for adding waveform elements, it was decided to expose TwistedPixel variable properties pertaining to the scope of a shape's attributes. For each shape you want to render the following properties are required:

Name

Range

Description

tp_shape_enabled 0 or 1 Turn a specific shape on or off for the frame's call to ProcessCustomShape
This is useful when your code has many shapes and you want to toggle a specific shapes display based on the music
tp_shape_sides > 0 Number of sides the polygon shape requires
tp_shape_additive 0 or 1 On render of the shape, add the shapes pixels to the existing pixels on the screen, otherwise draw over the existing pixels
tp_shape_thickOutline 0 or 1 Draw a thick border vs. normal width border
tp_shape_textured 0 or 1 One of the better features -- when 1 the previous frame will be rendered on the shape. Fo a nice example of this use see preset Geiss-Feedback.vbs by Ryan Geiss.
tp_shape_x 0 ... 1 The center position in the horizontal of the shape where 0 is the left and 1 is the right
tp_shape_y 0 ... 1 The center position in the vertical of the shape where 0 is the top and 1 is the bottom
tp_shape_rad > 0 default radius of the shape
tp_shape_ang 0 ... 6.28 Angle of the shape relative to its origin
tp_shape_r 0 ... 1 The red component of the color towards the center of the shape
tp_shape_g 0 ... 1 The green component of the color towards the center of the shape
tp_shape_b 0 ... 1 The blue component of the color towards the center of the shape
tp_shape_a 0 ... 1 The alpha component for the color components towards the center of the shape
tp_shape_r2 0 ... 1 The red component of the color towards the outer edge of the shape
tp_shape_g2 0 ... 1 The green component of the color towards the outer edge of the shape
tp_shape_b2 0 ... 1 The blue component of the color towards the outer edge of the shape
tp_shape_a2 0 ... 1 The alpha component for the color components towards the outer edge of the shape
tp_shape_border_r 0 ... 1 The red component of the border color
tp_shape_border_g 0 ... 1 The green component of the border color
tp_shape_border_b 0 ... 1 The blue component of the border color
tp_shape_border_a 0 ... 1 The alpha component of the border color


The body below is an example of drawing a shape per frame on the screen...from spangler - shape test.vbs
For a very good example of using multiple shapes as well as textures, review Geiss-Feedback.vbs

'''
' called during per-frame processing
' returning true will direct TwistedPixel to invoke this function again to get the next Custom Shape
' returning false will indicate that no more custom shapes are to be rendered by TwistedPixel
Function ProcessCustomShape

ProcessCustomShape = False

' set custom shape preset vars
tp_shape_enabled = 1
tp_shape_sides=4
tp_shape_additive=0
tp_shape_thickOutline=0
tp_shape_textured=1
tp_shape_x=0.500000
tp_shape_y=0.500000
tp_shape_rad=1.000000
tp_shape_ang=0.0000
tp_shape_r=1.000000
tp_shape_g=0.000000
tp_shape_b=0.000000
tp_shape_a=0.500000
tp_shape_r2=0.000000
tp_shape_g2=0.000000
tp_shape_b2=1.000000
tp_shape_a2=0.500000
tp_shape_border_r=1.000000
tp_shape_border_g=1.000000
tp_shape_border_b=0.000000
tp_shape_border_a=0.5000000

' apply some rotation and play around with the radius
tp_shape_ang = fT * (0.3 + 0.1 * sin(fT))
tp_shape_rad = tp_shape_rad + 0.1 * Sin(fT)

End Function
 

Conversion Utility

For those preset authors who might have millions of presets (OK well maybe a few thousand) I have written a small utility that will convert your Mildrop Presets to either VBScript or JavaScript. You can find the MD2TP.exe utility in the Plugins\TwistedPixel SDK folder -- upon running the utility you will want to select either single file mode or batch mode. When in single file mode you will convert one file at a time where both the source and destination scripts are displayed in the conversion windows. When in batch mode you are prompted to select a source folder and a destination folder. All the Milkdrop presets found in the source folder will be converted to the scripting language you selected and saved in the destination folder.

The conversion utility is not perfect and will sometimes fail to provide a conversion that is 1:1 with the original Milkdrop preset. For this reason I added the I hotkey to TwistedPixel that you can use to flag a preset as incomplete. When a preset is flagged as incomplete its filename will be prefixed with 'incomplete.conversion.' so that you can recognize it at a later time and open it for edit. I generally convert an entire folder of Milkdrop presets to an empty Plugins\TwistedPixel folder. I then run WinAMP and start loading presets for review using the hotkey n to advance to the next preset. When a preset looks to be faulty or displays a Script Engine runtime error I'll press the hotkey I and move on to the next preset.

This utility is still under development so please contact me if you find any issues with it or would like to see new features added.

*one note -- the current release of the conversion utility does not recognize custom shapes or custom waveforms.

 

Debugging your presets with Microsoft's Script Editor

How To Debug your scripts, including setting break points, adding variables to a watch window etc.

First off I must warn that setting up Microsoft Script Debugging is completely hit or miss -- I have tested the following on
3 machines and found success on two of them...oops just tested again, now its just one.


The recipe I used for setting up an environment for script debugging is the following:

Requirements:
-It seem version of WinAMP prior to release 2.91 will not work so ensure you have 2.91 or better
-For debugging script I use MSE, Microsoft Script Editor, version 10 which is provided as part of OfficeXP
-Download and install the latest script engines from Microsoft; currently release 5.6 and available for download
at http://msdn.microsoft.com/library/default.asp?url=/downloads/list/webdev.asp
-Download and install the current release of the Microsoft Script Debugger runtime files

for NT 4.0, 2000 and XP -- file: scd10en.exe
http://www.microsoft.com/downloads/details.aspx?FamilyId=2F465BE0-94FD-4569-B3C4-DFFDF19CCD99&displaylang=en

for 98 and Me -- file: ie401dbg.exe
http://www.microsoft.com/downloads/details.aspx?FamilyId=E606E71F-BA7F-471E-A57D-F2216D81EC3D&displaylang=en


How to start debugging your presets

. Open the preset you want to debug using any editor, I use MSE, and change the preset variable tp_fDuration to a value of -1
in method PresetInitialize. This will eliminate any transitions to another preset while you are debugging.

. Run WinAMP and open TwistedPixel using Shit+Ctrl+k and load the preset you want to debug by pressing the O key for Open
Preset ... do start the song just yet.

. Make sure that the TwistedPixel window has focus and is in the foreground (if your not sure then just click on the title
bar) then lauch the Script Debugger (I keep a shortcut to MSE7.exe on my desktop)

. From MSE click on menu items [Debug] and then [Processes] A window opens showing all the current processes available,
scroll down until you see the process WinAMP. The title next to WinAMP should be TwistedPixel, if it is not then use your
Alt-Tab key until you bring the TwistedPixel window to the foreground. Click on the TwistedPixel title bar and return to the
MSE Processes Window where you will need to click the refresh button to update the listing of processes and titles. Once you
find the WinAMP process with TwistedPixel title click on it and then press the Attach Button. A small window will be
presented to you where you have a check box option titled Script, leave this value checked. Below the check box in the pane
title 'The following programs will be debugged' should be listed one entry titled 'Script' if this value is missing then you
will not be establish a debug session and will have to join the millions of us who have tried and failed repeatedly before
lucking out.
Press the Select All button and Followed by the OK button. You will have returned to the Processes window where you'll now
press the 'break' button. This instructs the editor to break on the fist line of code that is to execute when you start a
song. Close the Processes window and Alt-Tab to the TwistedPixel window and press X to start a song.

. Upon the music starting you should be thrown into an MSE Debug session with the source code to your preset displayed to you
. At this point you will use the tools of the debugger to add variables to watch windows, set break points etc. I usually set
a break point on ProcessPerFrame and then press f5 to continue debugging.
. If at anytime the code suddenly disappears it only means the preset unloaded and although WinAMP is still running with
TwistedPixel there is no preset loaded for debugging ... no worry just go to the [Debug] menu and click on [Break All] this
will immediately break execution in the current preset running of which you can now set the break points, watches etc.

That is about it -- as I stated earlier there is no guarantee this will work on your machine, all I can do is wish you luck.
If you are fortunate enough to get setup with debugging then you will find authoring your presets a much easier task when you
have a debugger handy.

Lots of Luck, if you find other approaches to getting the script debugger working then please let me know so that I can post
for others to try.